Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FE] 회원탈퇴 페이지 퍼블리싱 및 api 연결 #858

Merged
merged 21 commits into from
Dec 18, 2024
Merged

Conversation

soi-ha
Copy link
Contributor

@soi-ha soi-ha commented Dec 17, 2024

issue

구현 이유

행동대장 서비스에 소셜 로그인을 도입하게 되었다. 그러면서 다양한 기능들이 새롭게 추가되어야 했는데, 그 중에 하나가 바로 회원탈퇴 기능이다.

회원 가입을 했기 때문에 당연히 회원 탈퇴를 할 수 있는 기능은 필수이다. 물론 자주 발생하지 않을 이벤트로 추측되지만, 그렇다고 개발자가 편하기 위해서 구글 스프레드 시트나 문의 사항으로 대체 할 수는 없다. 그렇게 된다면 사용자는 불편해지게 되면서 행동대장 서비스의 사용성이 안 좋아지게 될 것이다. 이는 우리 서비스에 대한 안 좋은 인식으로 퍼질 수 있다. 따라서, 회원탈퇴 기능도 사용자가 편할 수 있도록 구현하고자 신경썼다.

구현 과정

디자인

내가 개발하면서 가장 신경쓰는 부분은 UI/UX를 이쁘고 편하게 만드는 것이다. 그렇기에 디자인을 할 때, 토스를 참고하여 행동대장 서비스에 맞게 디자인을 신경썼다.

회원 탈퇴 기능에서 UI/UX 뿐 아니라 회원을 탈퇴하는 이유를 명확하게 수집하고자 했다. 단, 앞서 언급한 UX가 편하면서 탈퇴 이유 데이터를 수집할 수 있도록 해야 했다. 그러려면 사용자가 탈퇴하는 이유를 우리에게 말해줄 때 귀찮지 않게 해야 한다고 생각했다. 그래서 나는 탈퇴 이유를 큰 카테고리로 한 번 나누고, 그 카테고리 안에서 상세 내용을 선택할 수 있도록 다양한 선지를 사용자에게 주었다.

스크린샷 2024-12-17 15 01 31

구현 | checkbox 컴포넌트

  • Icon 컴포넌트에서 svg 아이콘의 색상을 변경할 수 없는 에러가 존재하여 수정하였습니다.
  • check icon이 기존에 존재했지만 다른 곳에서 사용하고 있지 않았고, 현재 사용해야 하는 check와 색상 및 사이즈가 달랐기 때문에 해당 컴포넌트에서 사용하는 check 아이콘으로 변경했습니다.
스크린샷 2024-12-17 15 10 48

구현 | textarea 컴포넌트

스크린샷 2024-12-17 15 11 10

구현 | 회원 탈퇴 페이지 펴블리싱

  • 회원 탈퇴를 위한 총 7가지의 페이지중 6가지 페이지 퍼블리싱 완료

    구현하지 않은 1가지 페이지는 현재 디자인 미확정 (사용방법 설명 페이지)

  • useRequestDeleteUser 생성

    회원탈퇴 api 연결을 위해 apis 폴더에 user.ts 파일 생성. 해당 파일에 requestDeleteUser 함수 생성. endpoint api/users

    useRequestDeleteUser에서 requestDeleteUser 요청이 정상적으로 실행되면 querykey kakaoLogin과 kakaoClientId를 무효화 시킴.

    const useRequestDeleteUser = () => {
      const queryClient = useQueryClient();
    
      const {mutateAsync, ...rest} = useMutation({
        mutationFn: () => requestDeleteUser(),
        onSuccess: () => {
          queryClient.invalidateQueries({queryKey: [QUERY_KEYS.kakaoLogin]});
          queryClient.invalidateQueries({queryKey: [QUERY_KEYS.kakaoClientId]});
        },
      });
    
      return {deleteAsyncUser: mutateAsync, ...rest};
    };

    회원 탈퇴 api를 실행하기 때문에 이와 연관된 소셜 로그인 queryKey를 무효화 했는데.. 다른 부분들도 무효화 시킬 필요가 있을지? 의견 부탁드립니다-

  • CheckBeforeWithdrawingStep

    해당 페이지는 회원 탈퇴를 진행하기 전 유의사항을 안내하는 페이지.

    const handleWithdraw = async () => {
        try {
          await deleteAsyncUser();
          handleMoveStep('withdrawalCompleted');
        } catch (error) {
          toast.error('회원 탈퇴에 실패했어요.', {
            showingTime: 3000,
            position: 'bottom',
          });
        }
      };

    해당 페이지의 탈퇴하기 버튼을 클릭하면 deleteAsyncUser가 실횅됨. 해당 함수가 정상적으로 실행되면 회원 탈퇴 완료 페이지(withdrawalCompleted) Step으로 이동함. 만약, 회원 탈퇴가 정상적으로 진행되지 않았을 경우 toast로 회원 탈퇴가 실패했다는 에러 메세지를 띄움.

구현 결과

스크린샷 2024-12-17 15 11 48 스크린샷 2024-12-17 15 12 14 스크린샷 2024-12-17 15 12 31 스크린샷 2024-12-17 15 12 43
  • 회원 탈퇴 api가 정상적으로 요청되었을 경우에만 해당 페이지로 이동함
스크린샷 2024-12-17 15 13 02

논의하고 싶은 것

회원탈퇴 routerUrl

회원탈퇴 페이지는 마이 페이지 하위에 존재하는 페이지입니다. 따라서 url도 ‘/mypage/withdraw’ 형식으로 가야한다고 생각했습니다. 다른 분들은 어떠신지 궁금합니다!

endpointPrefix 변경 논의

현재 endpointPrefix는 다음과 같이 존재합니다.

export const USER_API_PREFIX = '/api/events';
export const ADMIN_API_PREFIX = '/api/admin/events';
// 새로 추가된 api
export const MEMBER_API_PREFIX = '/api/users';

이번에 회원가입이 생기면서 새롭게 MEMBER_API_PREFIX를 추가하게 되었습니다.

현 상황의 네이밍은 혼동을 줄 수 있을 것이라고 판단했습니다.
events를 위한 prefix의 네이밍에 user를 사용하고 user를 위한 prefix에 member 네이밍을 사용하기 때문입니다.

따라서, 위와 같은 네이밍을 아래와 같이 변경하는건 어떨지 논의하고 싶습니다!

export const EVENT_API_PREFIX = '/api/events';
export const ADMIN_API_PREFIX = '/api/admin/events';
export const USER_API_PREFIX = '/api/users';

useRequestDeleteUser

위에서도 언급한것과 같이 queryKey를 소셜 로그인과 관련된 것만 무효화하면 될지.. 아님 추가적으로 행사들도 모두 무효화하는 것이 좋을지 다른 분들의 생각이 궁금합니다.

또한, 회원 탈퇴 api가 정상적으로 요청이 가는 것은 확인했으나, 진짜로! 정상적으로 회원탈퇴가 잘 이뤄졌는지는 확인하지 못했습니다~

구현과 관련된 공유 사항

  • 두번째 Step에서 Checkbox로 사유를 선택하거나 Textarea에 이유를 작성하는 등의 데이터 값들을 현재 상태관리는 하고 있지 않습니다. 이 부분은 다음 회의때 프엔+백엔드와 논의 후에 진행해야 할 것 같아서 제외했습니다. 다음 task에 같이 진행하도록 하겠습니다~

  • 로그아웃 기능에 관하여 (구현X)
    기존에 제가 맡은 task에는 로그아웃 기능도 같이 존재했습니다. 이건 저번에도 논의했다 싶이 token이 만료되면 자동 로그아웃이 진행될텐데, 로그아웃 기능이 필요한지? 의문이 들어 여러분과 더 이야기를 나누고 싶습니다. 우리 서비스는 여러 계정이 필요한 서비스가 아닌 단 하나의 계정만으로 서비스 이용이 가능합니다. 따라서 사용자가 의도적으로 로그아웃 기능을 사용하는 경우는 거의 없을 것으로 보입니다. 그렇기에 일단은 로그아웃 기능이 필요한지..? 의문이 들어서 따로 구현은 진행하지 않았습니다.

@soi-ha soi-ha added 🖥️ FE Frontend ⚙️ feat feature labels Dec 17, 2024
@soi-ha soi-ha added this to the v3.1.0 milestone Dec 17, 2024
@soi-ha soi-ha self-assigned this Dec 17, 2024
Copy link

Copy link
Contributor

@Todari Todari left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

스토리북에서도 component들 잘 구현해 주신 거 확인했어요~!
일하느라 바쁠텐데 고생 많았습니다 ㅜㅜ...
아마 퍼블리싱만 되어있어서, 본문에도 남겨주신 것 처럼 배포는 우선 제외하고 진행되겠네욘

routeUrl에 대해서 제안해 주신 의견도 너무 좋은 것 같아요! 하지만, 범용성을 생각했을 때 어떻게 나눠 놓는 것이 전체적으로 편할지 프엔 회의에서 별도로 의견을 들어봐도 좋을 것 같아요!!!
고생 많았습니다 :)

Comment on lines 48 to 54
// TODO: (@soha) 해당 요청은 user.ts 파일로 이동하는 건 어떨지?
export const requestPatchUser = async (args: RequestPatchUser) => {
return requestPatch({
endpoint: `/api/users`,
endpoint: MEMBER_API_PREFIX,
body: {
...args,
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

소하가 user.ts 의 파일을 만들고, user 관련 엔티티들을 여기에 모으는게 확실히 좋아 보이기는 해요!
제가 남겼던 #854 PR의 논의하기에서 디렉토리 관련 논의를 할 때 함께 논의해봐요! 프엔회의 합시다!!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오~ 이슈도 만들어두셨군용~!
그리고 이 부분은 다른 프엔분들 작업과도 겹쳐서 꼭 논의해봐야 할 것 같아요!

Comment on lines 16 to 19
// isFocus: {
// description: '',
// control: {type: 'boolean'},
// },
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

사용하지 않는 주석 있습니다~!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

으알멐ㄹ 감사합니댱

import {inputBoxAnimationStyle} from '../Input/Input.style';

interface Props {
height?: string;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

height를 효과적으로 건네받을 수 있는 좋은 방법을 생각해 봐야 할 것 같아요!!
우선은 통일된 방법이 별도로 없으니깐, 이렇게 해도 좋을 것 같아요!

Copy link
Contributor

@Todari Todari left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

스토리북에서도 component들 잘 구현해 주신 거 확인했어요~!
일하느라 바쁠텐데 고생 많았습니다 ㅜㅜ...
아마 퍼블리싱만 되어있어서, 본문에도 남겨주신 것 처럼 배포는 우선 제외하고 진행되겠네욘

routeUrl에 대해서 제안해 주신 의견도 너무 좋은 것 같아요! 하지만, 범용성을 생각했을 때 어떻게 나눠 놓는 것이 전체적으로 편할지 프엔 회의에서 별도로 의견을 들어봐도 좋을 것 같아요!!!
고생 많았습니다 :)

Copy link

Copy link
Contributor

@jinhokim98 jinhokim98 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

소하 고생했습니다! 혼자 탈퇴의 상황을 전부 생각해서 작성해주신 부분 넘 고생했어요! 소하가 논의점으로 남겨준 useRequestDeleteUser에 대한 부분을 코멘트로 작성해뒀으니 참고해주세요:) 이 부분을 수정해야 할 것 같아서 RC로 두겠습니다!
고생했어요~ 이따 회의 때 봐요!

@@ -18,6 +18,7 @@ export const ROUTER_URLS = {
event: EVENT,
login: '/login',
myPage: '/mypage',
withdraw: '/mypage/withdraw',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저도 동의합니다. 탈퇴 페이지는 마이페이지/탈퇴가 더 어울리는 것 같아요

Comment on lines +13 to +15
queryClient.invalidateQueries({queryKey: [QUERY_KEYS.kakaoLogin]});
queryClient.invalidateQueries({queryKey: [QUERY_KEYS.kakaoClientId]});
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 부분에 대해서 코멘트를 하나 드리고 싶어요.

/api/users/mine 대처 필요

소하가 작업한 내용에선 /api/users/mine을 호출하는 함수가 없는 것 같아요. 이 api는 유저의 정보를 불러오는 역할을 담당해요. 그래서 머지를 한 뒤에 이 api에 대해서 처리를 해줄 필요가 있을 것 같아요.

여기서 저는 invalidateQueries보다 removeQueries를 사용하는 것을 추천 드려요. 그 이유는 invalidate의 동작 방식 때문인데

탈퇴 전 마지막 페이지에서 유저 정보는 active하고 fresh한 데이터인데 여기서 invalidateQueries를 실행하면 이 컴포넌트가 unmount 되니깐 이제 inactive해지고 invalidateQueries에 의해 fresh -> stale 상태로 변화해요.
이를 그림으로 표현하면

inactive, stale 처리된 후 유저가 비회원으로 들어가서 행사를 만든 후 행사 페이지로 진입하면 /api/users/mine이 필요하므로 active, stale 상태가 됩니다. 이 때 서버로 요청을 하게 되고 그 동안은 이전 데이터를 화면에 보여주게 돼요. 그러면 이전 데이터가 보여지므로 프로필이 보였다가 새로운 데이터를 반영하면 프로필이 보이지 않아야 하므로 사라집니다. 결국 프로필이 있었다가 사라지는 깜빡이는 현상이 발생할 확률이 있어요.

그래서 invalidate 대신 remove를 사용해서 캐시 데이터 자체를 삭제하면 이전 데이터를 보여줄 것도 없어지게 되므로 이런 깜빡이는 현상을 방지할 수 있을 것이라 생각해요 :)

kakao invalidate에 대해서

kakaoClientId는 서버에서 고정된 값을 주는 api이고 클라이언트에서 이를 수정하는 메서드가 존재하지 않아요. 그래서 invalidate를 해줄 필요가 없고 이 코드를 지워도 될 것 같아요. 또한 kakaoLogin도 없어도 된다고 생각합니다. 왜냐하면 useRequestGetKakaoLogin hook의 queryKey는 [QUERY_KEYS.kakaoLogin, code] 이렇게 되어있고 여기서 코드는 카카오 로그인 api에서 주는 값입니다. 그래서 카카오에서 동일한 code를 줄 확률이 0이라고 생각하기 때문에 영향을 주지 않을 것이라 생각합니다. 또한 카카오 로그인 버튼을 누르는 순간 행동대장 사이트를 이탈하게 되어 갖고 있는 모든 캐시 데이터가 다 사라지게 됩니다. 그래서 돌아올 때 캐시 데이터가 다 사라져있기 때문에 이는 지워줄 필요가 없다고 생각해요. invalidate 하지 않아도 이미 캐시 데이터는 없어져있으니

결론: /api/users/mine api는 remove 해주고 kakao 관련은 둘 다 invalidate 해주지 않아도 돼요.

Comment on lines 2 to 4
export const USER_API_PREFIX = '/api/events';
export const ADMIN_API_PREFIX = '/api/admin/events';
export const MEMBER_API_PREFIX = '/api/users';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

맞아요 저도 이 부분 헷갈리는데 소하가 논의하고 싶은 점에 적어준 것처럼 바꿔주는 것이 더 좋을 것 같아요!

import {textareaStyle} from './Textarea.style';
import {TextareaProps} from './Textarea.type';

const Textarea: React.FC<TextareaProps> = forwardRef<HTMLTextAreaElement, TextareaProps>(function Textrea(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 코드에 대한 피드백은 아니지만 react 19 버전에선 forwardRef가 사라지고 직접 ref prop을 넘겨줄 수 있다고 하더라구요!

Comment on lines +3 to +10
export type WithdrawStep =
| 'withdrawReason'
| 'notUseService'
| 'unableToUseDueToError'
| 'cantFigureOutHowToUseIt'
| 'etc'
| 'checkBeforeWithdrawing'
| 'withdrawalCompleted';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

와.... 엄청 많은 작업을 하셨네요👏

Comment on lines +15 to +23
try {
await deleteAsyncUser();
handleMoveStep('withdrawalCompleted');
} catch (error) {
toast.error('회원 탈퇴에 실패했어요.', {
showingTime: 3000,
position: 'bottom',
});
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여기에 대해서 궁금한 점이 있어요! try catch를 사용하지 않고 deleteAsyncUser에서 에러가 발생했을 때 ErrorCatcher가 이를 처리하지 못해서 따로 처리해준 것인지 궁금해요

</Top>

<Flex flexDirection="column" gap="0.5rem">
<Text textColor="onTertiary">• 행동대장에서 관리했던 __님의 모든 개인정보를 다시 볼 수 없어요.</Text>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

나중에 여기 ~~님에 이름을 채워도 좋을 것 같아요. /api/users/mine api를 연동하면 유저 정보를 가져올 수 있어요!

</Top>
<div css={stepButtonGroupStyle}>
<StepButton stepText="사용하지 않는 서비스에요" nextStep={() => handleMoveStep('notUseService')} />
<StepButton stepText="오류가 생겨서 쓸 수 없어요" nextStep={() => handleMoveStep('unableToUseDueToError')} />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

우리에게 제일 슬픈 상황이네요ㅠㅠ

@pakxe pakxe merged commit 9f257ee into fe-dev Dec 18, 2024
2 checks passed
@pakxe pakxe deleted the feature/#848 branch December 18, 2024 07:46
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
Status: ✅ Done
Development

Successfully merging this pull request may close these issues.

4 participants